package service

import (
	"chat/conf"
	"chat/ret"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"strconv"
	"time"
)

const month = 60 * 60 * 24 * 30

type SendMsg struct {
	Type    int    `json:"type"`
	Content string `json:"content"`
}

type ReplyMsg struct {
	From    string `json:"from"`
	Code    int    `json:"code"`
	Content string `json:"content"`
}

type Client struct { // 用户类
	ID     string
	SendID string
	Dest   string
	Socket *websocket.Conn
	Send   chan []byte
}

type Group struct { //感觉起到一个   知道发送给哪些人的作用就好
	ID        string             //  超过 10000
	Members   map[string]bool    //  记录  群聊中的成员
	Clients   map[string]*Client //  记录在线的成员
	Broadcast chan *Broadcast    //  发给所有在线的成员

}

// 开始 自旋监听请求
func (group *Group) GroupInit() {
	for {
		select {

		case broadcast := <-group.Broadcast:
			//TODO 执行广播逻辑
			message := broadcast.Message
			//sendId := broadcast.Client.SendID

			sendCnt := 0

			for _, conn := range group.Clients {
				// 遍历 在线客户端集合    发送广播消息
				conn.Send <- message
				sendCnt++
			}
			fmt.Printf("累计发送给了群聊%s中 %d 个客户端", group.ID, sendCnt)
			content := "累计发送给了" + strconv.Itoa(sendCnt) + "个客户端"
			replyMsg := &ReplyMsg{
				Code:    ret.WebsocketOnlineReply, // 到时候随便赋一个 值吧   广播消息编码
				Content: content,
			}

			msg, _ := json.Marshal(replyMsg)

			//  写回广播消息 回执
			_ = broadcast.Client.Socket.WriteMessage(websocket.TextMessage, msg)

			//TODO  insert逻辑

		}

	}
}

// 接收消息
func (this *Client) Read() {
	defer func() {
		Manager.Unregister <- this
		_ = this.Socket.Close()
	}()
	for {
		this.Socket.PongHandler()
		sendMsg := new(SendMsg)
		fmt.Println("开始解析数据")

		err := this.Socket.ReadJSON(&sendMsg)
		if err != nil {
			log.Println("数据格式不正确", err)
			break
			//sendMsg.Type = 1
			//sendMsg.Content = "发送一条内容默认的消息" //  为什么死循环了   可能是因为socketReadJson 报错的话  就消费不掉   一直不能完成阻塞
		}
		if sendMsg.Type == 1 {
			// 发送消息
			//fmt.Printf("%s %s", this.SendID, this.ID)  这个每个id
			tmp, _ := strconv.Atoi(this.Dest)
			if tmp > 10000 {
				//执行群聊 逻辑

				var sendGroup *Group
				for _, group := range Manager.Groups {
					if group.ID == this.Dest {
						sendGroup = group
						break
					}
				}

				/*
					刚刚是类型不匹配的问题
				*/

				//现在要写一个  一直监听请求的逻辑
				sendGroup.Broadcast <- &Broadcast{Client: this, Message: []byte(sendMsg.Content)}

			} else if this.Dest == "10000" {
				fmt.Println("执行广播逻辑")
				fmt.Println(this.SendID)
				Manager.Broadcast <- &Broadcast{Client: this, Message: []byte(sendMsg.Content)}

			} else {
				r1, _ := conf.RedisClient.Get(this.ID).Result()
				r2, _ := conf.RedisClient.Get(this.SendID).Result()
				if r1 > "3" && r2 == "" { // 防止1频繁骚扰2
					replyMsg := ReplyMsg{
						Code:    ret.WebsocketLimit,
						Content: "达到限制",
					}
					msg, _ := json.Marshal(replyMsg)
					_ = this.Socket.WriteMessage(websocket.TextMessage, msg)
					continue
				} else {
					conf.RedisClient.Incr(this.ID)
					// 建立的连接缓存三个月
					_, _ = conf.RedisClient.Expire(this.ID, time.Hour*24*30*3).Result()
				}
				fmt.Println("这是在发消息前还是发消息后啊！")
				Manager.Broadcast <- &Broadcast{Client: this, Message: []byte(sendMsg.Content)}
			}

		} else if sendMsg.Type == 2 { // 获取历史消息
			// 获取10条历史消息    这东西差不到不赢啊

			fmt.Printf("mongdbName : %s  \r", conf.MongoDBName)
			results, _ := FindMany(conf.MongoDBName, this.SendID, this.ID, 10)
			fmt.Printf("%s %s %s\r", conf.MongoDBName, this.SendID, this.ID)
			if len(results) > 10 {
				results = results[:10]
			} else if len(results) == 0 {
				replyMsg := ReplyMsg{
					Code:    ret.WebsocketEnd,
					Content: "到底了",
				}
				msg, _ := json.Marshal(replyMsg)
				_ = this.Socket.WriteMessage(websocket.TextMessage, msg)
				continue
			}
			for _, result := range results {
				replyMsg := ReplyMsg{
					From:    result.From,
					Content: fmt.Sprintf("%s", result.Msg),
				}
				msg, _ := json.Marshal(replyMsg)
				_ = this.Socket.WriteMessage(websocket.TextMessage, msg)
			}
		} else if sendMsg.Type == 3 { // 首次查询时, 取出对方发来的所有未读
			results, err := FirstFindMsg(conf.MongoDBName, this.SendID, this.ID)
			if err != nil {
				log.Println(err)
			}
			fmt.Println("首次查询:", results)
			for _, result := range results {
				replyMsg := ReplyMsg{
					From:    result.From,
					Content: fmt.Sprintf("%s", result.Msg),
				}
				msg, _ := json.Marshal(replyMsg)
				_ = this.Socket.WriteMessage(websocket.TextMessage, msg)
			}
		}
	}
}

// 发送消息
func (this *Client) Write() {
	defer func() {
		_ = this.Socket.Close()
	}()
	for {
		select {
		case message, ok := <-this.Send:
			if !ok {

				fmt.Println("普通双方都在线的消息是这样发的吗")
				_ = this.Socket.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}
			//  从管道中读到了   就是该客户机收到消息了
			log.Println(this.ID, "接受消息:", string(message))
			replyMsg := ReplyMsg{
				Code:    ret.WebsocketSuccessMessage,
				Content: fmt.Sprintf("%s", string(message)),
			}
			msg, _ := json.Marshal(replyMsg)
			// 每一个请求都是一个groutine，如果有多个groutine同时请求并且要写回数据，就会出现这个错误
			_ = this.Socket.WriteMessage(websocket.TextMessage, msg) //TODO  此处有并发问题   不能并发写吗
		}
	}
}

// 广播类，包括广播内容和源用户
type Broadcast struct {
	Client  *Client
	Message []byte
	Type    int
}

// 用户管理
type ClientManager struct {
	Groups []*Group // 维护有哪些客户端

	Clients    map[string]*Client
	Broadcast  chan *Broadcast
	Reply      chan *Client
	Register   chan *Client
	Unregister chan *Client
}

// Message 信息转JSON (包括：发送者、接收者、内容)
type Message struct {
	Sender    string `json:"sender,omitempty"`
	Recipient string `json:"recipient,omitempty"`
	Content   string `json:"content,omitempty"`
}

var Manager = ClientManager{

	// group
	//TODO
	//Groups:     make([] *Group, 1010),
	Clients:    make(map[string]*Client), // 参与连接的用户，出于性能的考虑，需要设置最大连接数
	Broadcast:  make(chan *Broadcast),
	Register:   make(chan *Client),
	Reply:      make(chan *Client),
	Unregister: make(chan *Client),
}

func CreateID(uid, toUid string) string {
	return uid + "->" + toUid
}

func Handler(c *gin.Context) {
	uid := c.Query("uid")
	toUid := c.Query("toUid")

	var upgrader = websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool { // CheckOrigin解决跨域问题
			return true
		},
	}
	// 调用Upgrader.Upgrade使http协议, 升级成ws协议, 并返回一个*conn
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		http.NotFound(c.Writer, c.Request)
		return
	}
	// 创建一个用户实例
	client := &Client{
		ID:     CreateID(uid, toUid),
		SendID: CreateID(toUid, uid),
		Dest:   toUid,
		Socket: conn,
		Send:   make(chan []byte),
	}
	fmt.Println("WS协议升级完成")
	// 用户注册到用户管理上   这样就不会出现 并发写的问题
	//  出现并发写的时候    就搞一把锁
	//  群聊 上线草案
	for _, group := range Manager.Groups {
		if group.Members[uid] == true {
			fmt.Println("在 %s 群组完成用户%s 的上线\r", group.ID, uid)
			group.Clients[uid] = client

		}
	}

	if toUid != "10000" {
		Manager.Register <- client
	}

	// 使用conn收发消息
	go client.Read()
	go client.Write()
}
